package parser;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;

import parser.AltaRicaParseNode.CycleDetectionException;

import model.AltaRicaConst;
import model.AltaRicaDomain;
import model.altarica_fields.VarType;

import common.AltaRicaModel;
import common.listeners.AltaRicaModelListener;

public class AltaRicaParseModel {

    private HashMap<String, AltaRicaParseNode> hash;
    private HashMap<String, AltaRicaDomain> domains;
    private HashMap<String, AltaRicaConst> consts;
    private ArrayList<AltaRicaModelListener> _alatricaModelListener = null;
    private String _fileName = "";
    private AltaRicaParseModel _ast = null;
    private ArrayList<String> _subNodesCompiled = new ArrayList<String>();
    private boolean _parseError = false;
    public AltaRicaParseModel(String fileName) {
        this._fileName = fileName;
        this._alatricaModelListener = new ArrayList<AltaRicaModelListener>();
        this.hash = new HashMap<String, AltaRicaParseNode>();
        this.domains = new HashMap<String, AltaRicaDomain>();
        this.consts = new HashMap<String, AltaRicaConst>();
    }
    
    public void setParseError(boolean value) {
    	this._parseError = value;
    }
    
    public boolean getParseError() {
    	return this._parseError;
    }

    public void addNode(AltaRicaParseNode node) {

        this.hash.put(node.getName(), node);
    }

    public void addDomain(AltaRicaDomain domain) {

        this.domains.put(domain.getName(), domain);
    }

    public HashMap<String, AltaRicaParseNode> getParsedNodes() {

        return this.hash;
    }

    public Collection<AltaRicaParseNode> getNodes() {
        return Collections.unmodifiableCollection(this.getParsedNodes().values());
    }

    public Collection<AltaRicaDomain> getDomains() {

        return Collections.unmodifiableCollection(this.domains.values());
    }

    public AltaRicaDomain getDomain(String id) {

        return this.domains.get(id);
    }

    public void addConst(AltaRicaConst cnst) {

        this.consts.put(cnst.getId(), cnst);
    }

    public AltaRicaConst getConst(String id) {

        return this.consts.get(id);
    }

    public Collection<AltaRicaConst> getConsts() {
        return Collections.unmodifiableCollection(this.consts.values());
    }

    private void compileConsts(AltaRicaModel model) {

        for (AltaRicaConst cnst : this.consts.values()) {

            model.addConst(cnst);
        }
    }

    public void compile(AltaRicaModel model) throws CycleDetectionException {
     
        compileConsts(model);
        compileDomains(model);
        compileNodes(model);
        if (_alatricaModelListener != null) {
            for (AltaRicaModelListener listener : _alatricaModelListener) {
                listener.modelCompiled(getFileName());
            }
        }
    }

    public void clearCompiledSubNodes() {
    	_subNodesCompiled.clear();
    }

    public ArrayList<String> getSubNodesCompiled() {
    	return this._subNodesCompiled;
    }
    private void compileDomains(AltaRicaModel model) {

        String str = null;
        AltaRicaDomain dom = null;
        VarType type = null;

        for (AltaRicaDomain domain : getDomains()) {

            type = domain.getType();

            if (type.getVarType() == VarType.DOMAIN) {

                str = domain.getType().getDomain();

                dom = domains.get(str);

                if (dom == null) {
                    System.out.println("domain not found");
                }

                type = dom.getType();

                if (type == null) {
                    System.out.println("type not found");
                }

                domain.setType(type);
            }

            model.addDomain(domain);
        }
    }

    private void compileNodes(AltaRicaModel model) throws CycleDetectionException {

    	clearCompiledSubNodes();
        for (AltaRicaParseNode parsenode : getParsedNodes().values()) {

            if (parsenode.isCompiled() == false) {
                parsenode.compile(this);
            }
        }

        for (AltaRicaParseNode parsenode : getParsedNodes().values()) {
            model.addNode(parsenode.getNode());
        }
    }

    /**
     * @param alatricaModelListener the _alatricaModelListener to set
     */
    public void addAlatricaModelListener(AltaRicaModelListener alatricaModelListener) {
        this._alatricaModelListener.add(alatricaModelListener);
    }

    /**
     * @return the _fileName
     */
    public String getFileName() {
        return _fileName;
    }

    /**
     * @param fileName the _fileName to set
     */
    public void setFileName(String fileName) {
        this._fileName = fileName;
    }

    /**
     * @return the _ast
     */
    public AltaRicaParseModel getAst() {
        return _ast;
    }

    /**
     * @param ast the _ast to set
     */
    public void setAst(AltaRicaParseModel ast) {
        this._ast = ast;
    }
}
